﻿/*
 * =====================================================================================
 *
 *       Filename:  EpollServer.c
 *
 *    Description:  this is a epoll server example
 *
 *        Version:  1.0
 *        Created:  
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  
 *        Company:  
 *
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>

#include "ClientInfo.h"
#include "MyDb.h"

/*struct addrinfo 
{
	int              ai_flags;
	int              ai_family;
	int              ai_socktype;
	int              ai_protocol;
	size_t           ai_addrlen;
	struct sockaddr *ai_addr;
	char            *ai_canonname;
	struct addrinfo *ai_next;
}; */

static int create_and_bind(char* port)
{
	struct addrinfo hints;
	struct addrinfo*result,*rp;
	int s,sfd;

	memset(&hints,0,sizeof(struct addrinfo));
	hints.ai_family= AF_UNSPEC;/* Return IPv4 and IPv6 */
	hints.ai_socktype= SOCK_STREAM;/* TCP socket */
	hints.ai_flags= AI_PASSIVE;/* All interfaces */

	s = getaddrinfo(NULL, port, &hints, &result); //more info about getaddrinfo() please see:man getaddrinfo!
	if(s != 0)
	{
		fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(s));
		return -1;
	}
	for(rp=result; rp!=NULL; rp=rp->ai_next)
	{
		sfd = socket(rp->ai_family,rp->ai_socktype,rp->ai_protocol);
		if(sfd==-1)
			continue;
		printf("family:%d, socktype:%d, protocol:%d\n", rp->ai_family, rp->ai_socktype, rp->ai_protocol);
		if (rp->ai_addr)
		{
			s =bind(sfd,rp->ai_addr,rp->ai_addrlen);
			struct sockaddr_in *sin;
			sin = (struct sockaddr_in*)rp->ai_addr;
			char addr[20] = {0};
			inet_ntop(AF_INET, &sin->sin_addr, addr, INET_ADDRSTRLEN); 
			printf("address: %s\n", addr);
		}
		if(s ==0)
		{
			printf("bind to port:%s successful!\n", port);
			/* We managed to bind successfully! */
			break;
		}
		close(sfd);
	}

	if(rp== NULL)
	{
		fprintf(stderr,"Could not bind\n");
		return-1;
	}
	freeaddrinfo(result);
	printf("sfd: %d\n", sfd);
	return sfd;
}

static int make_socket_non_blocking(int sfd)
{
	int flags, s;
	flags = fcntl(sfd, F_GETFL,0);
	if(flags == -1)
	{
		perror("fcntl");
		return-1;
	}

	flags|= O_NONBLOCK;
	s =fcntl(sfd, F_SETFL, flags);
	if(s ==-1)
	{
		perror("fcntl");
		return-1;
	}
	return 0;
}

void acceptclient(epoll_event& event, int sfd, int efd)
{
	int s;
	while(1)
	{
		struct sockaddr in_addr;
		socklen_t in_len;
		int infd;
		char hbuf[NI_MAXHOST],sbuf[NI_MAXSERV];

		in_len = sizeof in_addr;
		infd = accept(sfd,&in_addr,&in_len);
		if(infd==-1)
		{
			if((errno== EAGAIN)||
					(errno== EWOULDBLOCK))
			{
				/* We have processed all incoming
				   connections. */
				break;
			}
			else
			{
				fprintf(stderr, "accept failed!\n");
				break;
			}
		}

		s =getnameinfo(&in_addr,in_len,
				hbuf,sizeof(hbuf),
				sbuf,sizeof(sbuf),
				NI_NUMERICHOST | NI_NUMERICSERV);
		if(s ==0)
		{
			printf("Accepted connection on descriptor %d "
					"(host=%s, port=%s)\n",infd,hbuf,sbuf);
			ClientInfo ci;
			ci.AddClient(infd, hbuf, sbuf);
		}

		/* Make the incoming socket non-blocking and add it to the
		   list of fds to monitor. */
		s = make_socket_non_blocking(infd);
		if(s ==-1)
		{
			fprintf(stderr, "make_socket_non_blocking failed\n");
			return;
		}

		event.data.fd=infd;
		event.events= EPOLLIN | EPOLLET;
		s = epoll_ctl(efd, EPOLL_CTL_ADD,infd,&event);
		if(s ==-1)
		{
			perror("epoll_ctl");
			return ;
		}
	}
}

void responseclient(int cfd, int efd)
{
	int s;
	int done =0;
	while(1)
	{
		printf("start to reading data from client\n");
		ssize_t count;
		char buf[512];
		count = read(cfd, buf, sizeof(buf));
		if(count == -1)
		{
			/* If errno == EAGAIN, that means we have read all
			   data. So go back to the main loop. */
			if(errno!= EAGAIN)
			{
				perror("read");
				done=1;
			}
			break;
		}
		else if(count ==0)
		{
			/* End of file. The remote has closed the
			   connection. */
			done=1;
			break;
		}
		/* Write the buffer to standard output */
		s = write(1,buf, count);
		if(s ==-1)
		{
			fprintf(stderr, "write the data from client failed\n");
			return;
		}
		printf("read data form client finished this round\n");
	}
	if(done)
	{
		printf("Closed connection on descriptor %d\n",cfd);
		/* Closing the descriptor will make epoll remove it
		   from the set of descriptors which are monitored. */
		close(cfd);
	}
}

#define MAXEVENTS 64
int main(int argc,char*argv[])
{
	int sfd;	// sfd: server fd
	int efd;	// efd: epoll fd
	int s;
	struct epoll_event event;
	struct epoll_event* events;

	if(argc!=2)
	{
		argv[1] = "18888";
		//fprintf(stderr,"Usage: %s [port]\n",argv[0]);
		//exit(EXIT_FAILURE);
	}

	sfd = create_and_bind(argv[1]);
	if( sfd == -1 )
	{
		printf("create and bind failed\n");
		return -1;
	}

	s = make_socket_non_blocking(sfd);
	if(s ==-1)
	{
		fprintf(stderr, "make_socket_non_blocking failed");
		return -1;
	}

	s = listen(sfd, SOMAXCONN);
	if(s ==-1)
	{
		perror("listen failed");
		return -1;
	}

	efd = epoll_create1(0);
	if(efd==-1)
	{
		perror("epoll_create");
		return -1;
	}

	event.data.fd=sfd;
	event.events= EPOLLIN | EPOLLET;
	s =epoll_ctl(efd, EPOLL_CTL_ADD,sfd,&event);
	if(s ==-1)
	{
		perror("epoll_ctl");
		return -1;
	}

	/* Buffer where events are returned */
	events = (epoll_event*)calloc(MAXEVENTS, sizeof(event));

	CMyDb db;
	db.Init();

	/* The event loop */
	while(1)
	{
		int n,i;
		n =epoll_wait(efd, events, MAXEVENTS,-1);
		for(i=0;i< n;i++)
		{
			if((events[i].events & EPOLLERR)||
					(events[i].events & EPOLLHUP)||
					(!(events[i].events & EPOLLIN)))
			{
				/* An error has occured on this fd, or the socket is not
				   ready for reading (why were we notified then?) */
				fprintf(stderr,"epoll error\n");
				close(events[i].data.fd);
				continue;
			}

			else if(sfd == events[i].data.fd)
			{
				/* We have a notification on the listening socket, which
				   means one or more incoming connections. */
				acceptclient(event, sfd, efd);
				continue;
			}
			else
			{
				/* We have data on the fd waiting to be read. Read and
				   display it. We must read whatever data is available
				   completely, as we are running in edge-triggered mode
				   and won't get a notification again for the same
				   data. */
				responseclient(events[i].data.fd, efd);
				
			}
		}
	}
	free(events);
	close(sfd);
	return EXIT_SUCCESS;
}
